package com.d4a5.mapping.google2
{
	import com.google.maps.overlays.MarkerOptions;
	import com.google.maps.overlays.PolygonOptions;
	import com.google.maps.overlays.PolylineOptions;
	import com.google.maps.styles.FillStyle;
	import com.google.maps.styles.GradientStyle;
	import com.google.maps.styles.StrokeStyle;
	
	import flash.events.Event;
	

	public class wmgOverlayLayerGradientRenderer extends wmgOverlayLayerRenderer
	{
		protected var _attribute:String;
		protected var _infos:Array = new Array();
		
		public static const calcPctLinear:int = 0;
		public static const calcPctLog:int = 1;
		public var calculatePercentageMethod:int = 0;
		
		protected var _minCalcValue:Number = Number.POSITIVE_INFINITY;
		protected var _maxCalcValue:Number = Number.NEGATIVE_INFINITY;
		protected var _sumCalcValue:Number = 0;
		protected var _countCalcValue:Number = 0;
		protected var _avgCalcValue:Number = 0;
		
		
		public function wmgOverlayLayerGradientRenderer()
		{
			super();
		}
		
		override public function getOptions(overlay:wmgOverlayLayerBaseShape, overlayDefaultOptions:Object=null):Object{
			
			var value:Number = overlay.attributes[ this._attribute ] as Number;
				
			if( isNaN( value ) ){
				return this.defaultOptions;
			}
			
			if( this._infos.length > 1 ){
				this._infos.sortOn( "value" , Array.NUMERIC );
				
				
				var minInfo:wmgOverlayLayerGradientInfo = this._infos[0];
				var maxInfo:wmgOverlayLayerGradientInfo = this._infos[this._infos.length-1];
				if( value < minInfo.value || value > maxInfo.value ){
					return this.defaultOptions;
				}
				
				maxInfo = null;
				var length:int = this._infos.length;
				for( var i:int = 1; i < length; i++ ){
					maxInfo = this._infos[i]; 
					if( value <= maxInfo.value ){
						return this.createNewOptions( value , minInfo , maxInfo ); 
					}
					minInfo = maxInfo;
				}
			}
			
			return this.defaultOptions;
			
		}
		
		
		
		
		
		public function get attribute() : String {
			return this._attribute;
		}
		public function set attribute( a:String ) : void {
			if( a != null && a != "" ){
				this._attribute = a.replace( /(\t|\n|\r| )+/g , "" );
				this._needsUpdating = "attribute";
				this.dispatchEvent( new Event( "renderer_changed" ) );
			}
		}
		
		
		public function get infos() : Array {
			return this._infos;
		}
		public function set infos( value:Array ) : void {
			this._infos = value;
		}
		
		
		protected function createNewOptions( value:Number  , minInfo:wmgOverlayLayerGradientInfo , maxInfo:wmgOverlayLayerGradientInfo ) : Object {
			var min:Number = minInfo.value;
			var max:Number = maxInfo.value;
			if( min == max ){
				return this.defaultOptions;
			}
			
			var minOpt:Object = minInfo.options;
			var maxOpt:Object = maxInfo.options;
			
			var pct:Number;
			if( this.calculatePercentageMethod == wmgOverlayLayerGradientRenderer.calcPctLog ){
				value = (value == 0) ? .000000001 : value;
				min = (min == 0) ?  .000000001 : min;
				max = (max == 0) ?  .000000001 : max;
				pct =  (Math.log(value) - Math.log(min)) / (Math.log(max) - Math.log(min))
			}
			else{
				pct = ( value - min ) / ( max - min );
			}
			
			if( minOpt is PolygonOptions && maxOpt is PolygonOptions ){
				return this.mergePolygonOptions( pct , minOpt as PolygonOptions , maxOpt as PolygonOptions );
			}
			else if( minOpt is MarkerOptions && maxOpt is MarkerOptions ){
				return this.mergeMarkerOptions( pct , minOpt as MarkerOptions , maxOpt as MarkerOptions );
			}
			else if( minOpt is PolylineOptions && maxOpt is PolylineOptions ){
				return this.mergePolylineOptions( pct , minOpt as PolylineOptions , maxOpt as PolylineOptions );
			}
			
			return this.defaultOptions;
		}
		
		
		protected function mergeFillStyles( pct:Number , min:FillStyle , max:FillStyle ) : FillStyle {
			var fill:FillStyle = new FillStyle();
			if( min == null && max == null ){
				return null;
			}
			else if( min == null  ){
				return ( pct < .5 ) ? null : max;
			}
			else if( max == null ){
				return ( pct >= .5 ) ? null : min;
			}
			
			fill.color = this.calculateColor( pct ,  min.color as int , max.color as int );
			fill.alpha = (pct * (Number(max.alpha) - Number(min.alpha))) + min.alpha;
			fill.gradient = this.mergeGradientStyles( pct , min.gradient , max.gradient );
			
			return fill;
		}
		
		
		protected function mergeStrokeStyles( pct:Number , min:StrokeStyle , max:StrokeStyle ) : StrokeStyle {
			var stroke:StrokeStyle = new StrokeStyle();
			if( min == null && max == null ){
				return null;
			}
			else if( min == null  ){
				return ( pct < .5 ) ? null : max;
			}
			else if( max == null ){
				return ( pct >= .5 ) ? null : min;
			}
			
			stroke.pixelHinting = ( pct < .5 ) ? min.pixelHinting : max.pixelHinting;
			stroke.thickness = (pct * (Number(max.thickness) - Number(min.thickness))) + min.thickness;
			stroke.color = this.calculateColor( pct ,  min.color as int , max.color as int );
			stroke.alpha = (pct * (Number(max.alpha) - Number(min.alpha))) + min.alpha;
			return stroke;
		}
		
		protected function mergeGradientStyles( pct:Number , min:GradientStyle , max:GradientStyle ) : GradientStyle {
			var grad:GradientStyle = new GradientStyle();
			var allOrNothing:GradientStyle = ( pct < .5 ) ? min : max;
			
			if( allOrNothing ){
				grad.alphas = allOrNothing.alphas;
				grad.colors = allOrNothing.colors;
				grad.focalPointRatio = allOrNothing.focalPointRatio;
				grad.interpolationMethod = allOrNothing.interpolationMethod;
				grad.matrix = allOrNothing.matrix;
				grad.ratios = allOrNothing.ratios;
				grad.spreadMethod = allOrNothing.spreadMethod;
				grad.type = allOrNothing.type;
				return grad;
			}
			return null;
		}
		
		protected function mergePolylineOptions( pct:Number , min:PolylineOptions , max:PolylineOptions ) : PolylineOptions {
			var polyline:PolylineOptions = new PolylineOptions();
			polyline.strokeStyle = this.mergeStrokeStyles( pct , min.strokeStyle , max.strokeStyle );
			polyline.geodesic = ((( pct < .5 ) ? min : max) as PolylineOptions).geodesic;
			
			return polyline;
		}
		
		protected function mergePolygonOptions( pct:Number , min:PolygonOptions , max:PolygonOptions ) : PolygonOptions {
			var polygon:PolygonOptions = new PolygonOptions();
			polygon.strokeStyle = this.mergeStrokeStyles( pct , min.strokeStyle , max.strokeStyle );
			polygon.fillStyle = this.mergeFillStyles( pct , min.fillStyle , max.fillStyle );
			polygon.tooltip = ((( pct < .5 ) ? min : max) as PolygonOptions).tooltip;
			
			return polygon;
		}
		
		protected function mergeMarkerOptions( pct:Number , min:MarkerOptions , max:MarkerOptions ) : MarkerOptions {
			var marker:MarkerOptions = new MarkerOptions();
			marker.strokeStyle = this.mergeStrokeStyles( pct , min.strokeStyle , max.strokeStyle );
			marker.fillStyle = this.mergeFillStyles( pct , min.fillStyle , max.fillStyle );
			
			var allOrNothing:MarkerOptions = ( pct < .5 ) ? min : max;
			if( allOrNothing ){
				marker.clickable = allOrNothing.clickable;
				marker.distanceScaling = allOrNothing.distanceScaling;
				marker.draggable = allOrNothing.draggable;
				marker.gravity = allOrNothing.gravity;
				marker.hasShadow = allOrNothing.hasShadow;
				marker.icon = allOrNothing.icon;
				marker.iconAlignment = allOrNothing.iconAlignment;
				marker.iconOffset = allOrNothing.iconOffset;
				marker.iconShadow = allOrNothing.iconShadow;
				marker.label = allOrNothing.label;
				marker.labelFormat = allOrNothing.labelFormat;
				marker.radius = allOrNothing.radius;
				marker.tooltip = allOrNothing.tooltip;
			}
			return marker;
		}
		
		protected function calculateColor( pct:Number , min:int , max:int ) : uint {
			var minColor:Object = { color : min , r : (min & 0xff0000) >> 16 , g : (min & 0xff00) >> 8 , b : (min & 0xff) };
			var maxColor:Object = { color : max , r : (max & 0xff0000) >> 16 , g : (max & 0xff00) >> 8 , b : (max & 0xff) };
			
			var differences:Object = new Object();
			differences.r = maxColor.r - minColor.r;
			differences.g = maxColor.g - minColor.g;
			differences.b = maxColor.b - minColor.b;
			
			var r:int = int((Number(differences.r) * Number(pct))+ Number(minColor.r));
			var g:int = int((Number(differences.g) * Number(pct))+ Number(minColor.g));
			var b:int = int((Number(differences.b) * Number(pct))+ Number(minColor.b));
			
			var color:uint = (r<<16)+(g<<8)+b;
			return color;
		}
		
		
		override public function startUpdating() : void {
			if( this._needsUpdating == "attribute" ){
				this._minCalcValue = Number.POSITIVE_INFINITY;
				this._maxCalcValue = Number.NEGATIVE_INFINITY;
				this._sumCalcValue = 0;
				this._countCalcValue = 0;
				this._avgCalcValue = 0;
			}
			else{
				super.startUpdating();
			}
		}
		override public function update( overlay:wmgOverlayLayerBaseShape ) : void {
			if( this._needsUpdating == "attribute" ){
				var value:Number = overlay[this._attribute];
				
				this._sumCalcValue += value;
				this._countCalcValue++;
				this._minCalcValue = ( value < this._minCalcValue ) ? value : this._minCalcValue;
				this._maxCalcValue = ( value > this._maxCalcValue ) ? value : this._maxCalcValue;
			}
			else{
				super.update( overlay );
			}
		}
		override public function endUpdating() : void {
			if( this._needsUpdating == "attribute" ){
				this._needsUpdating = null;
				this._avgCalcValue = this._sumCalcValue / this._countCalcValue;
			}
			else{
				super.endUpdating();
			}
		}
		
	}
}